/*
 * CAudioPresenter.cpp
 *
 *  Created on: Sep 6, 2011
 *      Author: welcome
 */

#include "CAudioPresenter.h"
#include <SLES/OpenSLES_IID.c>

namespace GGEngine {

SLObjectItf CAudioPresenter::sl = KD_NULL;
SLEngineItf CAudioPresenter::eng = KD_NULL;
SLObjectItf CAudioPresenter::outputMix = KD_NULL;
SLVolumeItf CAudioPresenter::out_vol = KD_NULL;


/* Initialize OpenSL ES Engine (should be called only once) */
int CAudioPresenter::initializeEngine()
{
	if(sl != KD_NULL)
	{
		return 0;
	}
	SLresult ret;

	/* Options to create OpenSL ES engine in thread-safe mode */
    SLEngineOption engineOption[] = {
		{
			(SLuint32) SL_ENGINEOPTION_THREADSAFE,
			(SLuint32) SL_BOOLEAN_TRUE
		}
	};

	/* Create OpenSL ES Engine */
	ret = slCreateEngine(&sl, 1, engineOption, 0, KD_NULL, KD_NULL);
	if(ret != SL_RESULT_SUCCESS)
	{
		kdLogMessage("!! slCreateEngine fault");
		return -1;
	}

	/* Realizing the SL Engine in synchronous mode. */
	ret = (*sl)->Realize(sl, SL_BOOLEAN_FALSE);
	if(ret != SL_RESULT_SUCCESS)
	{
		kdLogMessage("!! Realize fault");
		return -1;
	}

	/* Retrieve Engine interface */
	ret = (*sl)->GetInterface(sl, SL_IID_ENGINE, &eng);
	if(ret != SL_RESULT_SUCCESS)
	{
		kdLogMessage("!! GetInterface fault");
		return -1;
	}

	createOutputMix();
    return 0;
}

/* Release OpenSL ES Engine resources (should be called only once) */
void CAudioPresenter::releaseEngine()
{
	disposeOutputMix();

	if(sl != KD_NULL)
	{
		(*sl)->Destroy(sl);
		sl = KD_NULL;
	}
}

/* Mute or unmute the Output Mix */
void CAudioPresenter::setOutputMute(bool mute)
{
	(*out_vol)->SetMute(out_vol, (mute) ? SL_BOOLEAN_TRUE : SL_BOOLEAN_FALSE);
}

/* Get the mute state of the Output Mix */
bool CAudioPresenter::getOutputMute()
{
	SLboolean muted;
	(*out_vol)->GetMute(out_vol, &muted);
	return (muted) ? true : false;
}

/* private */

/* Create OutputMix object */
int CAudioPresenter::createOutputMix()
{
	if (outputMix != KD_NULL) {
		return 0;
	}

	SLresult ret;
	const SLboolean required[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
	SLInterfaceID iidArray[2] = {SL_IID_VOLUME, SL_IID_EQUALIZER};

	/* Create Output Mix */
    ret = (*eng)->CreateOutputMix(eng, &outputMix, 0, iidArray, required);
    if(ret != SL_RESULT_SUCCESS)
	{
    	kdLogMessage("!! CreateOutputMix fault");
    	return -1;
	}

	/* Realizing Output Mix */
	ret = (*outputMix)->Realize(outputMix, SL_BOOLEAN_FALSE);
	if(ret != SL_RESULT_SUCCESS)
	{
    	kdLogMessage("!! Realize fault");
		return -1;
	}

	/* Retrieve Volume interface for Output Mix */
	ret = (*outputMix)->GetInterface(outputMix, SL_IID_VOLUME, (void*)&out_vol);
	if(ret != SL_RESULT_SUCCESS)
	{
		kdLogMessage("get outputmix volume Interface error\n");
		return -1;
	}

	return 0;
}

/* Destroy OutputMix object */
void CAudioPresenter::disposeOutputMix()
{
	if(outputMix != KD_NULL)
	{
		(*outputMix)->Destroy(outputMix);
		outputMix = KD_NULL;
	}
}

/* Create PCM Player */
SLint32 CAudioPresenter::createPCMPlayer()
{
	SLDataSource datasrc;
	SLDataLocator_URI uri;
	SLDataFormat_MIME mime;
	SLDataSink datasink;
	SLDataLocator_OutputMix om;
	SLInterfaceID reqiid[] = {SL_IID_PLAY, SL_IID_SEEK, SL_IID_VOLUME};
	SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
	SLresult res = SL_RESULT_SUCCESS;

	uri.locatorType = SL_DATALOCATOR_URI;
	uri.URI         = (SLchar*)sound->getLocation(); // SLES does not specify "const"
	mime.formatType    = SL_DATAFORMAT_MIME;
	mime.mimeType      = (SLchar*)"audio/x-wav"; // SLES does not specify "const"
	mime.containerType = SL_CONTAINERTYPE_WAV;
	datasrc.pLocator = &uri;
	datasrc.pFormat  = &mime;

	om.locatorType = SL_DATALOCATOR_OUTPUTMIX;
	om.outputMix   = outputMix;

	datasink.pLocator = &om;
	datasink.pFormat  = KD_NULL;

	/* Create Audio Player */
    res = (*eng)->CreateAudioPlayer(eng, &player, &datasrc, &datasink, sizeof(reqiid)/sizeof(SLInterfaceID), reqiid, req);
    if(res != SL_RESULT_SUCCESS)
    {
        kdLogMessage("CreatePCMPlayer: CreatePCMPlayer() failed");
        return -1;
    }
	return 0;
}

/* Create Audio Player */
void CAudioPresenter::createPlayer()
{
	SLresult ret;

	if(player)
	{
		kdLogMessage("player create error [not player init]");
		return;
	}

	SLDataSource  fileSrc;
	SLDataSink    audOutSnk;
	SLDataLocator_OutputMix audOutLoc;

	/* Data Locator */
#ifdef MEDIA_DATA_LOAD_MEMORY
	SLDataLocator_Address fileLoc = { SL_DATALOCATOR_ADDRESS, (SLchar *)sound->getData(), sound->getDataLength() };
#else
	SLDataLocator_URI fileLoc = { SL_DATALOCATOR_URI, (SLchar *)sound->getLocation() };
#endif

	/* Data Format */
	SLchar* mime = NULL;
	SLuint8 contType = 0;
	switch(sound->getMediaType())
	{
	case MEDIA_SOUND_MIDI:
		mime = (SLchar *)"audio/midi";
		contType = SL_CONTAINERTYPE_SMF;
		break;
	case MEDIA_SOUND_WAV:
		mime = (SLchar *)"audio/x-wav";
		contType = SL_CONTAINERTYPE_WAV;
		break;
	case MEDIA_SOUND_MP3:
		mime = (SLchar *)"audio/mpeg";
		contType = SL_CONTAINERTYPE_MP3;
		break;
	case MEDIA_SOUND_OGG:
		mime = (SLchar *)"audio/ogg";
		contType = SL_CONTAINERTYPE_UNSPECIFIED;
		break;
	}
	SLDataFormat_MIME fileFmt = { SL_DATAFORMAT_MIME, mime, contType };

	/* input */
	fileSrc.pFormat = &fileFmt;
	fileSrc.pLocator = &fileLoc;

	/* output */
	audOutLoc.locatorType = SL_DATALOCATOR_OUTPUTMIX;
	audOutLoc.outputMix = outputMix;
	audOutSnk.pFormat = NULL;
	audOutSnk.pLocator = &audOutLoc;

	/* Interface */
	const SLboolean required2[4] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
	SLInterfaceID iidArray2[4] = {SL_IID_PLAY, SL_IID_SEEK, SL_IID_VOLUME, SL_IID_PITCH};

	if(sound->getMediaType() == MEDIA_SOUND_MIDI)
	{
		ret = (*eng)->CreateMidiPlayer(eng, &player,
									   &fileSrc,	/* MIDI data source */
									   NULL,		/* instrument bank source */
									   &audOutSnk,	/* Output device */
									   NULL,		/* vibra device */
									   NULL,		/* LED array device */
									   3,			/* The number of supported interfaces */
									   iidArray2,	/* Interface ID */
									   required2); /* Interface flag */
	}
	else if(sound->getMediaType() == MEDIA_SOUND_MP3 || sound->getMediaType() == MEDIA_SOUND_OGG)
	{
		ret = (*eng)->CreateAudioPlayer(eng, &player, &fileSrc, &audOutSnk, 3, iidArray2, required2);
	}
	else if(sound->getMediaType() == MEDIA_SOUND_WAV)
	{
		ret = createPCMPlayer();
	}
	else
	{
		kdLogMessage("type error\n");
		return;
	}
	if(ret != SL_RESULT_SUCCESS)
	{
		kdLogMessage("CreatePlayer error\n");
		return;
	}

	ret = (*player)->Realize(player, SL_BOOLEAN_FALSE);
	if(ret != SL_RESULT_SUCCESS)
	{
		kdLogMessage("Realize Player error\n");
		return;
	}

	/* Retrieve Player interface */
	ret = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf);
	if(ret != SL_RESULT_SUCCESS)
	{
		kdLogMessage("get player Interface error\n");
		return;
	}

	/* Retrieve Seek interface */
	ret = (*player)->GetInterface(player, SL_IID_SEEK, (void*)&seek);
	if(ret != SL_RESULT_SUCCESS)
	{
		kdLogMessage("get seek Interface error\n");
		return;
	}

	/* Retrieve Volume interface */
	ret = (*player)->GetInterface(player, SL_IID_VOLUME, (void*)&vol);
	if(ret != SL_RESULT_SUCCESS)
	{
		kdLogMessage("get volume Interface error\n");
		return;
	}

	/* Get the duration of the current content */
	ret = (*playItf)->GetDuration(playItf, &dur);
	if(ret != SL_RESULT_SUCCESS)
	{
		kdLogMessage("get duration error\n");
		dur = SL_TIME_UNKNOWN;
		return;
	}
}

/* public */

CAudioPresenter::CAudioPresenter()
{
	player = KD_NULL;
	playItf = KD_NULL;
	vol = KD_NULL;
	seek = KD_NULL;
	dur = 0;
	sound = KD_NULL;
	volume = 100;
	loopFlag = false;
}

CAudioPresenter::~CAudioPresenter()
{
	releasePlayResource();
}

/* Release Player's resources */
void CAudioPresenter::releasePlayResource()
{
	if (player != KD_NULL) {
		(*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
		(*player)->Destroy(player);
		player = NULL;
	}
}

/* Set the media information for the player */
void CAudioPresenter::setSound(CMediaSound* _sound)
{
	UTIL_ASSERT(_sound != NULL);
	if(sound == _sound)
	{
		return;
	}
	sound = _sound;
	releasePlayResource();
	createPlayer();
}

/* Get the current media information */
CMediaSound* CAudioPresenter::getMediaSound()
{
	return sound;
}

/* play the audio from the beginning */
void CAudioPresenter::play()
{
	play(0);
}

/* play the audio from the specified position */
void CAudioPresenter::play(int time)
{
	if(player == NULL)
	{
		kdLogMessage("not set sound");
		return;
	}

	if(time != 0)
	{
		setCurrentTime(time);
	}
	setVolume(volume);

	SLresult ret = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
	if(ret != SL_RESULT_SUCCESS)
	{
		kdLogMessage("play error\n");
		return;
	}
}

/* pause */
void CAudioPresenter::pause()
{
	SLuint32 state = getPlayState();
	if(state == SL_PLAYSTATE_PAUSED || state == SL_PLAYSTATE_STOPPED)
	{
		return;
	}

	(*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PAUSED);
}

/* stop */
void CAudioPresenter::stop()
{
	SLuint32 state = getPlayState();
	if(state == SL_PLAYSTATE_STOPPED)
	{
		return;
	}

	(*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
}

/* restart */
void CAudioPresenter::restart()
{
	if (getPlayState() != SL_PLAYSTATE_PAUSED) {
		return;
	}

	(*playItf)->SetPlayState(playItf, SL_PLAYSTATE_PLAYING);
}

/* Get the player's current head position */
int CAudioPresenter::getCurrentTime()
{
	if(player == NULL)
	{
		return 0;
	}

	if(getPlayState() == SL_PLAYSTATE_PLAYING)
	{
		SLuint32 pos;
		(*playItf)->GetPosition(playItf, &pos);
		return (int) pos;
	}

	return 0;
}

/* Get the duration of the content */
int CAudioPresenter::getTotalTime()
{
	if (player == NULL) {
		return 0;
	}

	return (int) dur;
}

/* Get the player's current play state */
SLuint32 CAudioPresenter::getPlayState()
{
	if (player == NULL) {
		return SL_PLAYSTATE_STOPPED;
	}

	SLuint32 state;
	(*playItf)->GetPlayState(playItf, &state);
	return state;
}

/* Set the volume level (0--100%) */
void CAudioPresenter::setVolume(int _volume)
{
	if(player != NULL)
	{
		if(_volume <= 0)
		{
			(*vol)->SetVolumeLevel(vol, SL_MILLIBEL_MIN);
			volume = 0;
		}
		else if(_volume >=100)
		{
			SLmillibel max_level;
			(*vol)->GetMaxVolumeLevel(vol, &max_level);
			(*vol)->SetVolumeLevel(vol, max_level);
			volume = 100;
		}
		else
		{
			KDfloat32 vol_f = 1000*(kdLogf((KDfloat32)_volume)/kdLogf(10.0f))-2000;
			(*vol)->SetVolumeLevel(vol, (SLmillibel)vol_f);
			volume = _volume;
		}
	}
}

/* Get the volume level (0--100%) */
int CAudioPresenter::getVolume()
{
	return volume;
}

/* Enable or disable looping */
void CAudioPresenter::setLoop(bool _loopFlag)
{
	loopFlag = _loopFlag;

	if(player != NULL)
	{
		SLresult ret;
		ret = (*seek)->SetLoop(seek, (loopFlag)? SL_BOOLEAN_TRUE : SL_BOOLEAN_FALSE, 0, SL_TIME_UNKNOWN);
		if(ret != SL_RESULT_SUCCESS)
		{
			kdLogMessage("SLSeekItf_ SetLoop() error");
			return;
		}
	}
}

/* Get the looping state */
bool CAudioPresenter::isLoop()
{
	return loopFlag;
}

/* Set the position of the playback head */
void CAudioPresenter::setCurrentTime(int time)
{
	if (player == NULL) {
		return;
	}

	SLresult ret = (*seek)->SetPosition(seek, time, SL_SEEKMODE_FAST);
	if(ret != SL_RESULT_SUCCESS)
	{
		kdLogMessage("seek error");
		return;
	}
}

/* Mute or unmute the player */
void CAudioPresenter::setMute(bool mute)
{
	(*vol)->SetMute(vol, (mute) ? SL_BOOLEAN_TRUE : SL_BOOLEAN_FALSE);
}

/* Get the mute state of the player */
bool CAudioPresenter::getMute()
{
	SLboolean muted;
	(*vol)->GetMute(vol, &muted);
	return (muted) ? true : false;
}

} /* namespace GGEngine */
